const std = @import("std");

const CodeWriter = @import("CodeWriter.zig");
const Command = @import("Command.zig");

pub fn main() !void {
    var gpa: std.heap.GeneralPurposeAllocator(.{}) = .{};
    defer std.debug.assert(gpa.deinit() == .ok);
    const allocator = gpa.allocator();

    const args = try std.process.argsAlloc(allocator);
    defer std.process.argsFree(allocator, args);

    if (args.len < 2) {
        std.log.err("Usage: {s} <dir/file.vm>", .{args[0]});
        return;
    }

    const filename = args[1];
    const cwd = std.fs.cwd();
    const file = try cwd.openFile(filename, .{});
    defer file.close();

    const stat = try file.stat();
    var asm_filename: []u8 = undefined;
    var src_dir: std.fs.Dir = undefined;
    var use_startup_code: bool = false;

    if (stat.kind == .file) {
        const noextension = filename[0 .. filename.len - 2]; // file name without extension "vm"
        asm_filename = try std.mem.concat(allocator, u8, &.{ noextension, "asm" });
        src_dir = try cwd.openDir(std.fs.path.dirname(filename) orelse "./", .{ .iterate = true });
    } else if (stat.kind == .directory) {
        asm_filename = try std.mem.concat(allocator, u8, &.{ filename, "/", std.fs.path.basename(filename), ".asm" });
        src_dir = try cwd.openDir(filename, .{ .iterate = true });
        use_startup_code = true;
    } else {
        unreachable;
    }
    defer allocator.free(asm_filename);
    defer src_dir.close();

    // truncate asm file for writing
    const asmfile = try cwd.createFile(asm_filename, .{ .truncate = true });
    defer asmfile.close();
    const writer = asmfile.writer();

    var cw: CodeWriter = .init(writer);
    if (use_startup_code) cw.startupCode();

    const linebuf = try allocator.alloc(u8, 1024);
    defer allocator.free(linebuf);

    // iterate over the src dir to find all .vm files in it
    var iter = src_dir.iterate();
    while (try iter.next()) |entry| {
        if (entry.kind == .file and std.mem.endsWith(u8, entry.name, ".vm")) {
            const vmfile = try src_dir.openFile(entry.name, .{});
            defer vmfile.close();

            const reader = vmfile.reader();
            try cw.setFileName(entry.name[0 .. entry.name.len - 3]); // file name without extension ".vm"

            // translate
            while (try getline(reader, linebuf)) |line| {
                if (line.len == 0) continue;
                try writer.print("// {s}\n", .{line});

                const cmd: Command = try .parse(line);
                try cw.translate(cmd);
            }
        }
    }
}

fn getline(reader: std.fs.File.Reader, buffer: []u8) !?[]const u8 {
    // 显式指定变量类型为 []const u8，把 []u8 赋值给它
    // 因为下面的 trimRight 返回 []const u8，不能把 []const u8 赋值给 []u8
    var line: []const u8 = (try reader.readUntilDelimiterOrEof(buffer, '\n')) orelse return null;

    // remove comments
    line = std.mem.sliceTo(line, '/');

    // trim annoying windows-only carriage return character and whiteSpace
    return std.mem.trimRight(u8, line, " \t\r");
}
